package com.xyrl.project.common.common.service.impl;

import cn.hutool.core.lang.Assert;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xyrl.common.base.exception.XyrlException;
import com.xyrl.common.util.ObjectUtil;
import com.xyrl.common.util.RedisUtil;
import com.xyrl.common.util.StringUtil;
import com.xyrl.project.common.common.dao.AttachmentMapper;
import com.xyrl.project.common.common.entity.Attachment;
import com.xyrl.project.common.common.entity.AttachmentImageUse;
import com.xyrl.project.common.common.enu.AttImageUseType;
import com.xyrl.project.common.common.service.AttachmentImageUseService;
import com.xyrl.project.common.common.service.AttachmentService;
import com.xyrl.project.common.common.vo.AttachmentVo;
import com.xyrl.project.common.constant.CacheConstant;
import com.xyrl.project.common.constant.XyrlConstant;
import com.xyrl.third.aliyun.config.AliYunProperties;
import com.xyrl.third.aliyun.enu.EContentType;
import com.xyrl.third.aliyun.service.AliOssService;
import com.xyrl.third.aliyun.util.OssUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;


/**
 * @author zhongp
 * @version 1.0
 * @className AttachmentServiceImpl.java
 * @company 重庆星纭融链科技公司
 * @date 2019-12-27 16:39:32
 * @Description 附件
 */
@Service
public class AttachmentServiceImpl extends ServiceImpl<AttachmentMapper, Attachment> implements AttachmentService {

    @Autowired
    AliOssService aliOssService;
    @Autowired
    private AliYunProperties aliYun;
    @Autowired
    private AttachmentImageUseService attachmentImageUseService;


    @Override
    public AttachmentVo addAttachment(MultipartFile file, Long userId, String userType, String module) throws XyrlException {
        Attachment attachment = new Attachment();
        String fileName = file.getOriginalFilename();
        attachment.setName(fileName);
        attachment.setUserType(userType);
        String extName = getExtName(fileName);
        String contentType = EContentType.getType(extName);
        attachment.setType(contentType);
        attachment.setCreateUserId(userId);
        attachment.setCreateTime(new Date());
        attachment.setStatus(XyrlConstant.ATTACHMENT_STATUS_CREATE);
        try {
            String path = generFileName(module, extName);
            aliOssService.uploadFile(file.getInputStream(), path);
            attachment.setBucketName(aliYun.getOss().getBucketName());
            attachment.setPath(path);
        } catch (IOException e) {
            throw new XyrlException(e.getMessage());
        } finally {
            save(attachment);
        }

        AttachmentVo vo = new AttachmentVo(attachment.getId(), attachment.getPath(), OssUtil.showUrl(aliYun.getOss().getBucketName(), attachment.getPath()), fileName);
        String key = CacheConstant.BASIC_ATT_GET + vo.getId();
        RedisUtil.setData(key, vo, CacheConstant.NUM_10, TimeUnit.MINUTES);
        return vo;
    }

    /**
     * 取得文件的扩展名
     *
     * @param fileName 文件名称
     * @return
     */
    private String getExtName(String fileName) {
        // 没扩展名
        if (fileName.lastIndexOf(".") == -1) {
            return "";
        }
        //返回文件扩展名
        return fileName.substring(fileName.lastIndexOf(".") + 1, fileName.length());
    }

    /**
     * 重新生成文件名称
     *
     * @param extName 文件扩展名
     * @return
     */
    private String generFileName(String module, String extName) {
        //生成文件目录
        String fileDir = "";
        if (ObjectUtil.isNotEmpty(module)) {
            fileDir = module;
        }
        //生成目录按 年/月/日存放
        Calendar calendar = Calendar.getInstance();
        if (ObjectUtil.isEmpty(fileDir)) {
            fileDir = StringUtil.concat(calendar.get(Calendar.YEAR) + "");
        } else {
            fileDir = StringUtil.concat(fileDir, "/", calendar.get(Calendar.YEAR) + "");
        }
        fileDir = StringUtil.concat(fileDir, "/", (calendar.get(Calendar.MONTH) + 1) + "");
        fileDir = StringUtil.concat(fileDir, "/", calendar.get(Calendar.DAY_OF_MONTH) + "");
        String uuid = UUID.randomUUID().toString();
        //判断扩展名是否为空
        if (ObjectUtil.isEmpty(extName)) {
            return StringUtil.concat(fileDir, "/", uuid);
        }
        return StringUtil.concat(fileDir, "/", uuid, ".", extName);
    }

    @Override
    public AttachmentVo findById(long id) {
        String key = CacheConstant.BASIC_ATT_GET + id;
        AttachmentVo vo = RedisUtil.getData(key, AttachmentVo.class);
        if (null == vo) {
            Attachment attachment = super.getById(id);
            if (null == attachment) {
                return null;
            }
            vo = new AttachmentVo(attachment.getId(), attachment.getPath(), OssUtil.showUrl(aliYun.getOss().getBucketName(), attachment.getPath()), attachment.getName());
            RedisUtil.setData(key, vo, CacheConstant.NUM_10, TimeUnit.MINUTES);
        }
        return vo;
    }

    @Override
    public List<AttachmentVo> findListByIdList(List<Long> ids) {
        List<AttachmentVo> list = new ArrayList<>();
        ids.forEach(id -> {
            if (null != id) {
                AttachmentVo attachmentVo = findById(id);
                if (null != attachmentVo) {
                    list.add(attachmentVo);
                }
            }
        });
        return list;
    }

    @Override
    public AttachmentVo findByPath(String path) {
        String key = CacheConstant.BASIC_ATT_GET + path;
        AttachmentVo vo = RedisUtil.getData(key, AttachmentVo.class);
        if (null != vo) {
            return vo;
        }
        LambdaQueryWrapper<Attachment> wrapper = new LambdaQueryWrapper();
        wrapper.eq(Attachment::getPath, path);
        Attachment attachment = super.getOne(wrapper);
        if (null == attachment) {
            return null;
        }
        vo = new AttachmentVo(attachment.getId(), attachment.getPath(), OssUtil.showUrl(aliYun.getOss().getBucketName(), attachment.getPath()), attachment.getName());
        RedisUtil.setData(key, vo, CacheConstant.NUM_10, TimeUnit.MINUTES);
        return vo;
    }

    @Override
    public List<AttachmentVo> findListByPathList(List<String> pathList) {
        List<AttachmentVo> list = new ArrayList<>();
        pathList.forEach(path -> {
            if (null != path) {
                list.add(findByPath(path));
            }
        });
        return list;
    }

    public void deleteAttImageUseByIdList(List<Long> idList) {
        // 删除附件使用
        LambdaQueryWrapper<AttachmentImageUse> lqWrapper = new LambdaQueryWrapper();
        lqWrapper.in(AttachmentImageUse::getAttId, idList);
        attachmentImageUseService.remove(lqWrapper);
        changeAttachmentStatus(idList,XyrlConstant.ATTACHMENT_STATUS_DEL);
    }

    public void deleteAttImageUseById(Long id) {
        // 删除附件使用
        LambdaQueryWrapper<AttachmentImageUse> lqWrapper = new LambdaQueryWrapper();
        lqWrapper.eq(AttachmentImageUse::getAttId, id);
        attachmentImageUseService.remove(lqWrapper);
        changeAttachmentStatus(Arrays.asList(id),XyrlConstant.ATTACHMENT_STATUS_DEL);
    }

    public void saveAttImageUseList(long useId, AttImageUseType useType, List<AttachmentVo> attachmentVoList) {
        List<AttachmentImageUse> list = new ArrayList<>();
        List<Long> attIds = new ArrayList<>();
        attachmentVoList.forEach(attachmentVo -> {
            AttachmentImageUse imageUse = new AttachmentImageUse();
            imageUse.setCreateTime(new Date());
            imageUse.setAttId(attachmentVo.getId());
            imageUse.setPath(attachmentVo.getPath());
            imageUse.setUseId(useId);
            imageUse.setUseType(useType.name());
            imageUse.setScene(useType.getScene());
            list.add(imageUse);
            attIds.add(attachmentVo.getId());
        });
        attachmentImageUseService.saveBatch(list);
        //设置图片批量
        changeAttachmentStatus(attIds,XyrlConstant.ATTACHMENT_STATUS_USE);
    }

    @Override
    public void handleAttImageUseAttId(long useId, AttImageUseType useType, long addAttId) {
        handleAttImageUseAttId(useId, useType, addAttId, null);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void handleAttImageUseAttId(long useId, AttImageUseType useType, long addAttId, Long delAttId) {
        // 判断附件是否存在
        AttachmentVo attachmentVo = this.findById(addAttId);
        Assert.isTrue(null != attachmentVo, "未找到" + useType.getText());

        // 判断添加的和删除附件是否相同
        boolean equallyFlag = null != delAttId && addAttId == delAttId.longValue();
        // 不相同
        if (!equallyFlag) {
            this.saveAttImageUseList(useId, useType, Arrays.asList(attachmentVo));
        }
        // 删除附件存在
        if (null != delAttId) {
            AttachmentVo vo = this.findById(delAttId);
            if (null != vo) {
                this.deleteAttImageUseById(vo.getId());
            }
        }
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void handleAttImageUsePath(long useId, AttImageUseType useType, String addAttPath, String delAttPath) {
        if (ObjectUtil.isEmpty(addAttPath)) {
            return;
        }
        // 判断附件是否存在
        AttachmentVo attachmentVo = this.findByPath(addAttPath);
        Assert.isTrue(null != attachmentVo, "未找到" + useType.getText());

        // 判断新旧附件是否相同
        boolean equallyFlag = addAttPath.equals(delAttPath);
        // 不相同
        if (!equallyFlag) {
            this.saveAttImageUseList(useId, useType, Arrays.asList(attachmentVo));
        }
        // 旧附件存在
        if (!equallyFlag && ObjectUtil.isNotEmpty(delAttPath)) {
            AttachmentVo vo = this.findByPath(delAttPath);
            if (null != vo) {
                this.deleteAttImageUseById(vo.getId());
            }
        }
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void handleAttImageUseAttId(long useId, AttImageUseType useType, List<Long> addAttIdList, List<Long> delAttIdList) {
        // 新增附件
        if (ObjectUtil.isNotEmpty(addAttIdList)) {
            // 判断附件是否存在
            List<AttachmentVo> newAttVoList = this.findListByIdList(addAttIdList);
            Assert.isTrue(addAttIdList.size() == newAttVoList.size(), "未找到" + useType.getText());
            this.saveAttImageUseList(useId, useType, newAttVoList);
        }
        // 删除附件
        if (ObjectUtil.isNotEmpty(delAttIdList)) {
            List<AttachmentVo> newAttVoList = this.findListByIdList(delAttIdList);
            if (ObjectUtil.isNotEmpty(newAttVoList)) {
                this.deleteAttImageUseByIdList(delAttIdList);
            }
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void handleAttImageUsePath(long useId, AttImageUseType useType, List<String> addAttPathList, List<String> delAttPathList) {
        // 新增附件
        if (ObjectUtil.isNotEmpty(addAttPathList)) {
            // 判断附件是否存在
            List<AttachmentVo> newAttVoList = this.findListByPathList(addAttPathList);
            Assert.isTrue(addAttPathList.size() == newAttVoList.size(), "未找到" + useType.getText());
            this.saveAttImageUseList(useId, useType, newAttVoList);
        }
        // 删除附件
        if (ObjectUtil.isNotEmpty(delAttPathList)) {
            List<AttachmentVo> newAttVoList = this.findListByPathList(delAttPathList);
            if (ObjectUtil.isNotEmpty(newAttVoList)) {
                // 获取 AttachmentVo 集合中的 id 集合
                List<Long> ids = newAttVoList.stream().map(AttachmentVo::getId).collect(Collectors.toList());
                this.deleteAttImageUseByIdList(ids);
            }
        }
    }

    @Override
    public void changeImageUsePath(long useId, AttImageUseType useType, String addAttPath, String delAttPath) {
        //如果新增图片与旧图片为同一值 则不进行处理(包括两个均为空）
        if (StringUtil.equals(addAttPath, delAttPath)) {
            return;
        }
        //判断是否添加图片
        if (ObjectUtil.isNotEmpty(addAttPath)) {
            // 判断附件是否存在
            AttachmentVo attachmentVo = this.findByPath(addAttPath);
            Assert.isTrue(null != attachmentVo, "未找到" + useType.getText());
            this.saveAttImageUseList(useId, useType, Arrays.asList(attachmentVo));
        }
        //判断原图片是否为空 原图片不为空则进行原图片删除处理
        if (ObjectUtil.isNotEmpty(delAttPath)) {
            AttachmentVo vo = this.findByPath(delAttPath);
            if (null != vo) {
                this.deleteAttImageUseById(vo.getId());
            }
        }
    }

    /**
     * 设置附件状态
     *
     * @param attIds 附ids
     * @param status 状态
     */
    private void changeAttachmentStatus(List<Long> attIds, int status) {
        UpdateWrapper<Attachment> up = new UpdateWrapper<>();
        up.set("status", status);
        up.in("id", attIds);
        baseMapper.update(new Attachment(), up);
    }
}
